package hw2.descrack;

import hw2.FileIO;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.*;
import java.nio.charset.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.SecretKeySpec;

public class Cracker
{

    private static byte[] cipherText;

    /**
     * @param args
     * @throws IOException
     */
    // public static void main(String[] args) throws Exception {
    //
    // // for ()
    // SecretKey desKey = new
    // SecretKeySpec(getBytesFromFile("hw2des.bin"),"DES");
    //
    // Cipher aesDec = Cipher.getInstance("DES/ECB/PKCS5Padding");
    // aesDec.init(Cipher.DECRYPT_MODE, desKey);
    //
    // byte[] htmlFile = getBytesFromFile("hw2.II.html.enc");
    //
    // byte[] text = aesDec.doFinal(htmlFile);
    // System.out.println(new String(text));
    // }
    //
    public static void main( String[] args ) throws Exception {
        byte[] keyBytes = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00 };

        cipherText = FileIO.getBytesFromFile( "hw2des.bin" );

        for( int i = 0; i < 0xFF; i++ ) {
            for( int j = 0; j < 0xFF; j++ ) {
                keyBytes[0] = (byte) i;
                keyBytes[1] = (byte) j;
                System.out.print( tryDecode( keyBytes, cipherText ) );
                // System.out.println(++line + ") keyBytes:" + new
                // String(keyBytes));

            }

            continue;
        }

    }

    private static String tryDecode( byte[] keyBytes, byte[] cipherText )
            throws InvalidKeyException, NoSuchAlgorithmException,
            NoSuchPaddingException {
        String result = "";
        SecretKeySpec key = new SecretKeySpec( keyBytes, "DES" );
        Cipher cipher = Cipher.getInstance( "DES/ECB/PKCS5Padding" );

        cipher.init( Cipher.DECRYPT_MODE, key );
        byte[] plainText = new byte[cipher.getOutputSize( cipherText.length )];
        int ptLength = 0;
        try {
            ptLength = cipher.update( cipherText, 0, cipherText.length,
                    plainText, 0 );
        } catch( ShortBufferException e ) {
            e.printStackTrace();
        }
        try {
            ptLength += cipher.doFinal( plainText, ptLength );
        } catch( IllegalBlockSizeException e ) {
            e.printStackTrace();
        } catch( ShortBufferException e ) {
            e.printStackTrace();
        } catch( BadPaddingException e ) {
            // e.printStackTrace();
            // ignore padding problems, skip this key
        }

        if( isASCII( plainText ) ) {
            result = "plain : " + new String( plainText ) + " key: "
                    + printKey(keyBytes) + "\n";
        }
        return result;
    }

    private static String printKey( byte[] keyBytes ) {
        String result = "[";
        for(int i = 0; i<keyBytes.length; i++){
            result += (int) keyBytes[i] + "][";
        }
        result = result.substring( 0, result.length() - 1  );
        return result;
    }

    // Returns the contents of the file in a byte array.
    public static byte[] getBytesFromFile( String filename ) throws IOException {
        File file = new File( filename );
        InputStream is = new FileInputStream( file );

        // Get the size of the file
        long length = file.length();

        if( length > Integer.MAX_VALUE ) {
            // File is too large
        }

        // Create the byte array to hold the data
        byte[] bytes = new byte[(int) length];

        // Read in the bytes
        int offset = 0;
        int numRead = 0;
        while( offset < bytes.length
                && (numRead = is.read( bytes, offset, bytes.length - offset )) >= 0 ) {
            offset += numRead;
        }

        // Ensure all the bytes have been read in
        if( offset < bytes.length ) {
            throw new IOException( "Could not completely read file " + filename );
        }

        // Close the input stream and return bytes
        is.close();
        return bytes;
    }

    private static boolean isASCII( byte[] input ) {
        CharsetDecoder charset = Charset.forName( "US-ASCII" ).newDecoder();
        try {
            CharBuffer charBuff = charset.decode( ByteBuffer.wrap( input ) );
            charBuff.toString();
        } catch( CharacterCodingException e ) {
            return false;
        }
        return true;

    }

}
